from datetime import datetime, date, timedelta, time
from typing import List, Any
from ortools.linear_solver import pywraplp as lp

from .base_class import Shift, ShiftStyle, SchedulingStaff, WorkTime, ScheduleTask, \
    Department, GuaranteeStep
from .util import message


RelaxId = -1

RelaxTime = WorkTime(
    id=RelaxId, 
    name="休息", 
    time_section_list=[], 
    required_staff_cnt=0)

class ScheduleSolver:
    def __init__(self,
            start_date: date,
            end_date: date,
            staff_list: List[SchedulingStaff],
            main_shift: Shift,
            aux_shift: Shift,
            department: Department,
            guarantee_step_list: List[GuaranteeStep],
            exist_summary:dict,
            arrange_data: Any = None,
            time_limit: int = 10000
            ):
        self._start_date = start_date
        self._end_date = end_date
        self._staff_list = staff_list
        self._main_shift = main_shift
        self._aux_shift = aux_shift
        self._department = department
        self._guarantee_step_list = guarantee_step_list
        self._exist_summary = exist_summary
        self._arrange_data = arrange_data
        self._time_limit = time_limit

        self._elaborate = False # 默认控制台不输出中间信息
        self._status = ScheduleSolver.IDLE
        self._msg = []

        self._unavail_main = []
        for i in range(self.staff_cnt):
            unavail_list = self.staff_leave_check(staff_list[i], self._main_shift)
            self._unavail_main.append(unavail_list)

        self._unavail_aux = []
        for i in range(self.staff_cnt):
            unavail_list = self.staff_leave_check(staff_list[i], self._aux_shift)
            self._unavail_aux.append(unavail_list)

    IDLE = 0
    MAIN_OPTIMAL = 1
    MAIN_INFEASIBLE = 2
    AUX_OPTIMAL = 3
    AUX_INFEASIBLE = 4

    @property
    def is_main_static(self) -> bool:
        return self._main_shift.style.name == ShiftStyle.Static.name

    @property
    def is_aux_static(self) -> bool:
        return self._aux_shift.style.name == ShiftStyle.Static.name

    @property
    def schedule_days_cnt(self) -> int:
        return (self._end_date - self._start_date).days + 1

    @property
    def staff_cnt(self) -> int:
        return len(self._staff_list)

    @property
    def main_duty_days(self) -> int:
        return self._main_shift.duty_days

    @property
    def main_rest_days(self) -> int:
        return self._main_shift.rest_days

    @property
    def main_avg_duty_cnt(self) -> float:
        return self.main_duty_days / (self.main_duty_days + self.main_rest_days) * self.schedule_days_cnt

    @property
    def main_shift_days(self):
        return self.main_duty_days + self.main_rest_days

    @property
    def main_worktime_cnt(self) -> int:
        return len(self._main_shift.worktime_list)

    @property
    def aux_duty_days(self) -> int:
        return self._aux_shift.duty_days

    @property
    def aux_rest_days(self) -> int:
        return self._aux_shift.rest_days

    @property
    def aux_avg_duty_cnt(self) -> float:
        return self.aux_duty_days / (self.aux_duty_days + self.aux_rest_days) * self.schedule_days_cnt
        
    @property
    def aux_shift_days(self):
        return self.aux_duty_days + self.aux_rest_days

    @property
    def aux_worktime_cnt(self) -> int:
        return len(self._aux_shift.worktime_list)

    @property
    def days_summary(self):
        summary = {}    # 工作总结

        for staff in self._staff_list:
            for schedule_task in staff.work_schedule:
                work_date_str = schedule_task.work_date

                if work_date_str not in summary.keys():
                    summary[work_date_str] = {}
    
                work_time_name = schedule_task.work_time.name
                if work_time_name not in summary[work_date_str].keys(): 
                    summary[work_date_str][work_time_name] = 1
                else:
                    summary[work_date_str][work_time_name] += 1

        return summary

    @property
    def status(self):
        return self._status

    @property
    def guarantee_step_list(self):
        return self._guarantee_step_list

    def calc_main_result(self, var_matrix):
        for i in range(self.staff_cnt):
            for j in range(self.schedule_days_cnt):
                workFlag = False
                for k in range(self.main_worktime_cnt):
                    staff = self._staff_list[i]
                    work_date = self._start_date + timedelta(days=j)

                    if var_matrix[i][j][k].solution_value() == 1:
                        work_time = self._main_shift.worktime_list[k]
                        schedule_task = ScheduleTask(
                            work_date=work_date,
                            staff=staff,
                            work_time=work_time,
                            shift=self._main_shift
                        )
                        staff.add_schedule(schedule_task)
                        workFlag = True
                        continue

                if not workFlag:
                    schedule_task = ScheduleTask(
                        work_date=work_date,
                        staff=staff,
                        work_time=RelaxTime,
                        shift=self._main_shift
                    )
                    staff.add_schedule(schedule_task)
        self._status = ScheduleSolver.MAIN_OPTIMAL

    def add_shift_sequence_rule(self, solver, var_matrix):
        duty_days = self.main_duty_days
        rest_days = self.main_rest_days
        ratio = int(self.main_worktime_cnt / duty_days)

        # !! 需要检查ratio或其倒数不是整数的情况，处理此异常

        work_arrange_cnt = ratio * (duty_days + rest_days)

        for i in range(self.staff_cnt):
            unavail_list = self._unavail_main[i]

            # 员工开始上班时间和算法开始排班时间的差值
            time_delta = (self._start_date - self._staff_list[i].start_date).days

            # 通过日期推算应执行的班型
            for j in range(self.schedule_days_cnt):
                tmp_index = time_delta + j
                if tmp_index >= 0:
                    if ratio > 0:
                        index = tmp_index % work_arrange_cnt
                        quotient = index // (duty_days + rest_days)
                        remainder = index % (duty_days + rest_days)
                        if remainder < duty_days:
                            worktime_index = quotient * duty_days + remainder
                            if (j, worktime_index) not in unavail_list:
                                solver.Add(var_matrix[i][j][worktime_index] == 1)
                    else:
                        remainder = tmp_index % (duty_days + rest_days)
                        if remainder < duty_days:
                            worktime_index = remainder % self.main_worktime_cnt
                            if (j, worktime_index) not in unavail_list:
                                solver.Add(var_matrix[i][j][worktime_index] == 1)

        return solver, var_matrix

    def staff_leave_check(self, staff, shift):
        if not staff.leave_list:
            return []
        
        worktime_list = shift.worktime_list
        worktime_cnt = len(worktime_list)
        schedule_s_datetime = datetime.combine(self._start_date, time(0, 0))
        schedule_e_datetime = datetime.combine(self._end_date, time(23, 59))

        unavail_list = []
        for l in staff.leave_list:
            leave_s_datetime = datetime.combine(l.start_time.date(), time(0, 0))
            leave_e_datetime = datetime.combine(l.end_time, time(23, 59))

            # 比较排班时间窗和请假时间窗
            s_datetime = max(leave_s_datetime, schedule_s_datetime)
            e_datetime = min(leave_e_datetime, schedule_e_datetime)
            if s_datetime < e_datetime:
                s_offset = (s_datetime - schedule_s_datetime).days
                e_offset = (e_datetime - schedule_s_datetime).days + 1

                for j in range(s_offset, e_offset):
                    for k in range(worktime_cnt):
                        first_time = worktime_list[k].time_section_list[0].start_time
                        last_time = worktime_list[k].time_section_list[-1].end_time
                        # 是否跨天
                        cross_day = max([ts.is_cross_day for ts in worktime_list[k].time_section_list])
                        
                        wt_s_dt = schedule_s_datetime + \
                            timedelta(days=j, hours=first_time.hour, minutes=first_time.minute)
                        wt_e_dt = schedule_s_datetime + \
                            timedelta(days=j+cross_day, hours=last_time.hour, minutes=last_time.minute)
                        
                        s_dt = max(wt_s_dt, leave_s_datetime)
                        e_dt = min(wt_e_dt, leave_e_datetime)

                        if s_dt < e_dt:
                            # Staff unavaliable in Shift_k of Day_j
                            unavail_list.append((j, k))
        return unavail_list

    def add_staff_startdate_rule(self, solver, var_matrix):
        for i in range(self.staff_cnt):
            # 员工开始上班时间和算法开始排班时间的差值
            time_delta = (self._start_date - self._staff_list[i].start_date).days

            unavail_list = self._unavail_main[i]

            for j in range(self.schedule_days_cnt):
                work_flag = False

                # 判断员工当天是否上班
                if time_delta + j >= 0:
                    remainder = (time_delta + j) % self.main_shift_days
                    w_c = self._main_shift.work_cycle

                    if w_c:
                        if w_c[remainder]:
                            work_flag = True
                    else:
                        if (time_delta + j) % self.main_shift_days < self.main_duty_days:
                            work_flag = True

                if work_flag:
                    avail_list = [k for k in range(self.main_worktime_cnt) if (j,k) not in unavail_list]
                    if not avail_list:
                        solver.Add(solver.Sum([var_matrix[i][j][k] for k in range(self.main_worktime_cnt)]) == 0)
                    else:
                        solver.Add(solver.Sum([var_matrix[i][j][k] for k in avail_list]) == 1)
                else:
                    solver.Add(solver.Sum([var_matrix[i][j][k] for k in range(self.main_worktime_cnt)])
                               == 0)

        return solver, var_matrix

    def add_balance_rule(self, solver, var_matrix):
        worktime_list = self._main_shift.worktime_list
        worktime_cnt = len(worktime_list)
        # duty_days = self.main_duty_days
        # rest_days = self.main_rest_days

        # 员工 Shift 均衡
        min_var = solver.NumVar(0, self.schedule_days_cnt, "Max work time")
        max_var = solver.NumVar(0, self.schedule_days_cnt, "Min work time")
        for i in range(self.staff_cnt):
            for k in range(worktime_cnt):
                solver.Add(solver.Sum([var_matrix[i][j][k] for j in range(self.schedule_days_cnt)])
                            + self._exist_summary[i]["worktime_summary"][k] >= min_var)
                solver.Add(solver.Sum([var_matrix[i][j][k] for j in range(self.schedule_days_cnt)])
                            + self._exist_summary[i]["worktime_summary"][k] <= max_var)

        obj = solver.Objective()
        obj.SetCoefficient(max_var, 1)
        obj.SetCoefficient(min_var, -1)
        
        # # 排班天数 * 上班百分比 / 班型个数
        # shift_avg = self.schedule_days_cnt * duty_days / \
        #     (duty_days+rest_days) / worktime_cnt
        # for i in range(self.staff_cnt):
        #     for k in range(worktime_cnt):
        #         solver.Add(solver.Sum([var_matrix[i][j][k] for j in range(self.schedule_days_cnt)])
        #                     >= shift_avg - 1)
        #         solver.Add(solver.Sum([var_matrix[i][j][k] for j in range(self.schedule_days_cnt)])
        #                    <= shift_avg + 1)

        return solver, var_matrix

    def solve_main(self) -> None:
        if not self._main_shift:
            return

        # # 0. 逻辑检查：某天所需人数之和，是否小于等于当天上班的人数
        # required_staff_per_day = sum([worktime.required_staff_cnt for worktime in self._main_shift.worktime_list])
        # check = [0 for j in range(self.schedule_days_cnt)]
        # for i in range(self.staff_cnt):
        #     time_delta = (self._start_date - self._staff_list[i].start_date).days

        #     for j in range(self.schedule_days_cnt):
        #         # 判断员工当天是否在上班
        #         if (time_delta + j) % self.main_shift_days < self.main_duty_days:
        #             check[j] += 1

        # for j in range(self.schedule_days_cnt):
        #     if check[j] < required_staff_per_day:
        #         self._status = ScheduleSolver.MAIN_INFEASIBLE
                
        #         error_date = self._start_date + timedelta(j)
        #         msg = f"主班 {error_date} 上班员工人数不足。主班排班无可行解!"
        #         self._msg += [msg]
        #         print(msg)

        #         return self._status

        # 1. 严格排班1，添加限制条件（1.员工开始上班日期。2.按班型顺序排班。）
        if self.is_main_static:
            solver, var_matrix = self.create_main_basic_solver()
            solver, var_matrix = self.add_staff_startdate_rule(solver, var_matrix)
            solver, var_matrix = self.add_shift_sequence_rule(solver, var_matrix)
        
            status = solver.Solve()
            if status == lp.Solver.OPTIMAL:
                self.calc_main_result(var_matrix)
                print("主班固定班制，严格排班求解完成!")
                return self._status

            msg = f"主班固定班制，严格排班无可行解!"
            self._msg += [msg]
            print(msg)

        # 2. 轮转班制
        else:
            # 限制条件：1.员工开始上班日期。2.班次均衡
            solver, var_matrix = self.create_main_basic_solver()
            solver, var_matrix = self.add_staff_startdate_rule(solver, var_matrix)
            solver, var_matrix = self.add_balance_rule(solver, var_matrix)
        
            solver.EnableOutput()
            status = solver.Solve()
            if status == lp.Solver.OPTIMAL:
                self.calc_main_result(var_matrix)
                print("主班轮转，严格排班求解完成!")
                return self._status

            msg = "主班轮转，严格排班无可行解!"
            self._msg += [msg]
            print(msg)

            # 限制条件：1.员工开始上班日期
            solver, var_matrix = self.create_main_basic_solver()
            solver, var_matrix = self.add_staff_startdate_rule(solver, var_matrix)
        
            status = solver.Solve()
            if status == lp.Solver.OPTIMAL:
                self.calc_main_result(var_matrix)
                print("主班轮转，严格排班2求解完成!")
                return self._status

            # msg = "主班轮转，严格排班2无可行解!"
            # self._msg += [msg]
            # print(msg)

        # # 3. 严格排班不可行，转而求其次，使用基本排班
        # solver, var_matrix = self.create_main_basic_solver()
        # if self._elaborate:
        #     solver.EnableOutput()
        # solver.SetTimeLimit(100000)
        # status = solver.Solve()
        # if status == lp.Solver.OPTIMAL:
        #     self.calc_main_result(var_matrix)
        #     print("主班基本排班求解完成!")
        #     return self._status
        
        # 4. 排班问题无可行解
        self._status = ScheduleSolver.MAIN_INFEASIBLE
        msg = "主班无可行解!"
        self._msg += [msg]
        print(msg)
        raise Exception(msg)
        return self._status

    def create_main_basic_solver(self) -> None:
        worktime_list = self._main_shift.worktime_list
        worktime_cnt = len(worktime_list)

        # 新建solver
        solver = lp.Solver("Schedule Solver",
                           lp.Solver.CBC_MIXED_INTEGER_PROGRAMMING)
        if self._elaborate:
            solver.EnableOutput()  

        # 变量矩阵
        var_matrix = [[[solver.IntVar(0, 1, f"Emp{i}_Day{j}_Shift{k}") 
            for k in range(worktime_cnt)] 
            for j in range(self.schedule_days_cnt)] 
            for i in range(self.staff_cnt)]

        # 约束条件
        # 每天的上班人数大于等于所需人数
        for j in range(self.schedule_days_cnt):
            for k in range(worktime_cnt):
                cnt = worktime_list[k].required_staff_cnt
                if (not cnt) or (cnt < 0):
                    cnt = 0
                solver.Add(solver.Sum([var_matrix[i][j][k] for i in range(self.staff_cnt)])
                           >= cnt)

        # 员工每次上班只上一个 Shift
        for i in range(self.staff_cnt):
            for j in range(self.schedule_days_cnt):
                solver.Add(solver.Sum([var_matrix[i][j][k] for k in range(worktime_cnt)])
                               <= 1)

        # 员工必须满足上M天，休N天的条件 
        round_cnt = self.schedule_days_cnt - self.main_shift_days
        if round_cnt >= 0:  # 排班时间范围大于一个班制周期
            for i in range(self.staff_cnt):
                for l in range(round_cnt):
                    solver.Add(
                        solver.Sum([var_matrix[i][j][k] 
                            for j in range(l, l+self.main_shift_days) 
                            for k in range(self.main_worktime_cnt)])
                        <= self.main_duty_days)
                        # == self.main_duty_days)   # 因为需要考虑休假，不一定能满足＂上Ｍ天＂的约束

        # 目标函数
        obj = solver.Objective()
        for i in range(self.staff_cnt):
            for j in range(self.schedule_days_cnt):
                for k in range(worktime_cnt):
                    obj.SetCoefficient(var_matrix[i][j][k], 1)
        obj.SetMinimization()

        return solver, var_matrix

    def calc_aux_result(self, var_matrix):
        for i in range(self.staff_cnt):
            for j in range(self.schedule_days_cnt):
                for k in range(self.aux_worktime_cnt):
                    staff = self._staff_list[i]
                    work_date = self._start_date + timedelta(days=j)

                    if var_matrix[i][j][k].solution_value() == 1:
                        work_time = self._aux_shift.worktime_list[k]
                        schedule_task = ScheduleTask(
                            work_date=work_date,
                            staff=staff,
                            work_time=work_time,
                            shift=self._main_shift,
                            is_overwork=True,
                        )
                        staff._work_schedule[j] = schedule_task
                        break
        self._status = ScheduleSolver.AUX_OPTIMAL

    def solve_aux(self):
        if not self._aux_shift:
            return

        # 1. 严格排班，添加限制条件（前一天未上主班的员工，不安排副班）
        solver, var_matrix = self.create_aux_basic_solver()
        for i in range(self.staff_cnt):
            for j in range(1, self.schedule_days_cnt):
                if (self._staff_list[i].work_schedule[j-1].work_time.id == RelaxId):
                    for k in range(self.aux_worktime_cnt):
                        solver.Add(var_matrix[i][j][k] == 0)

        # solver.EnableOutput()
        solver.SetTimeLimit(self._time_limit)
        status = solver.Solve()
        # 接受最优解和可行解。可行解的出现，是因为运算时间到达限制，而未算出最优解
        if status == lp.Solver.OPTIMAL or status == lp.Solver.FEASIBLE:
            if status == lp.Solver.OPTIMAL:
                print("OPTIMAL")
            if status == lp.Solver.FEASIBLE:
                print("FEASIBLE")

            self.calc_aux_result(var_matrix)
            print("副班严格排班求解完成!")
            self._status = ScheduleSolver.AUX_OPTIMAL
            return self._status   
        msg = "副班严格排班无可行解!"
        self._msg += [msg]
        print(msg)
        raise Exception(msg)
       
        # # 2. 严格排班不可行，转而求其次，使用基本排班
        # solver, var_matrix = self.create_aux_basic_solver()
        # status = solver.Solve()
        # if status == lp.Solver.OPTIMAL:
        #     self.calc_aux_result(var_matrix)
        #     print("副班基本排班求解完成!")
        #     return status

        # # 3. 排班问题无可行解
        self._status = ScheduleSolver.AUX_INFEASIBLE
        # msg = "副班无可行解!"
        # self._msg += [msg]
        # print(msg)
        return self._status

    def create_aux_basic_solver(self) -> int:
        worktime_list = self._aux_shift.worktime_list
        worktime_cnt = self.aux_worktime_cnt

        # 副班排班方案
        solver = lp.Solver("Aux Schedule Solver", lp.Solver.CBC_MIXED_INTEGER_PROGRAMMING)

        # 变量矩阵
        var_matrix = [[[solver.IntVar(0, 1, f"Emp{i}_Day{j}_Shift{k}") 
            for k in range(worktime_cnt)] 
            for j in range(self.schedule_days_cnt)] 
            for i in range(self.staff_cnt)]

        # 约束条件
        # 每天的上班人数满足约束
        for j in range(self.schedule_days_cnt):
            for k in range(worktime_cnt):
                solver.Add(solver.Sum([var_matrix[i][j][k] for i in range(self.staff_cnt)]) 
                    == worktime_list[k].required_staff_cnt)

        # 员工每次上班至多只上一个 Shift
        for i in range(self.staff_cnt):
            for j in range(self.schedule_days_cnt):
                solver.Add(solver.Sum([var_matrix[i][j][k] for k in range(worktime_cnt)])
                    <= 1)

        # 员工 Shift 均衡
        # 算法基于以下理解： 副班没有上几休几，每个班型所需人数，即是每天此班型的所需人数
        min_var = solver.NumVar(0, self.schedule_days_cnt, "Max work time")
        max_var = solver.NumVar(0, self.schedule_days_cnt, "Min work time")
        for i in range(self.staff_cnt):
            for k in range(worktime_cnt):
                solver.Add(solver.Sum([var_matrix[i][j][k] for j in range(self.schedule_days_cnt)])
                            >= min_var)
                solver.Add(solver.Sum([var_matrix[i][j][k] for j in range(self.schedule_days_cnt)])
                           <= max_var)

        # 上主班的员工，不安排副班
        for i in range(self.staff_cnt):
            for j in range(self.schedule_days_cnt):
                if not (self._staff_list[i].work_schedule[j].work_time.id == RelaxId):   # 不是休息
                    for k in range(worktime_cnt):
                        solver.Add(var_matrix[i][j][k] == 0)

        # 目标函数
        obj = solver.Objective()
        obj.SetCoefficient(max_var, 1)
        obj.SetCoefficient(min_var, -1)
        for i in range(self.staff_cnt):
            for j in range(self.schedule_days_cnt):
                for k in range(worktime_cnt):
                    obj.SetCoefficient(var_matrix[i][j][k], 1)
        obj.SetMinimization()

        return solver, var_matrix

    def solve_next_day(self):
        worktime_list = self._aux_shift.worktime_list
        worktime_cnt = self.aux_worktime_cnt

        # 副班排班方案
        solver = lp.Solver("Aux Schedule Solver", lp.Solver.CBC_MIXED_INTEGER_PROGRAMMING)

        # 变量矩阵
        var_matrix = [[solver.IntVar(0, 1, f"Emp{i}_Shift{k}") 
            for k in range(worktime_cnt)] 
            for i in range(self.staff_cnt)]

        # 约束条件
        # 加班人数满足约束
        for k in range(worktime_cnt):
            solver.Add(solver.Sum([var_matrix[i][k] for i in range(self.staff_cnt)]) 
                == worktime_list[k].required_staff_cnt)

        # 员工每次上班至多只上一个 Shift
        for i in range(self.staff_cnt):
            solver.Add(solver.Sum([var_matrix[i][k] for k in range(worktime_cnt)])
                <= 1)

        # 员工 Shift 均衡
            # 暂以月为周期，31天为上限            
        min_var = solver.NumVar(0, 31, "Max work time")
        max_var = solver.NumVar(0, 31, "Min work time")
        for i in range(self.staff_cnt):
            for k in range(worktime_cnt):
                solver.Add(var_matrix[i][k]
                           + self._exist_summary[i]["worktime_summary"][k]
                           >= min_var)
                solver.Add(var_matrix[i][k]
                           + self._exist_summary[i]["worktime_summary"][k]
                           <= max_var)

        # 已有上班安排的员工，不安排加班
        exist_staff_ids = [arr["userId"] for arr in self._arrange_data]
        for i in range(self.staff_cnt):
            if self._staff_list[i].id in exist_staff_ids:
                for k in range(worktime_cnt):
                    solver.Add(var_matrix[i][k] == 0)

        # 目标函数
        obj = solver.Objective()
        obj.SetCoefficient(max_var, 1)
        obj.SetCoefficient(min_var, -1)
        # for i in range(self.staff_cnt):
        #     for j in range(self.schedule_days_cnt):
        #         for k in range(worktime_cnt):
        #             obj.SetCoefficient(var_matrix[i][j][k], 1)
        obj.SetMinimization()

        status = solver.Solve()
        if status == lp.Solver.OPTIMAL:
            self.calc_nextday_result(var_matrix)
            print("加班排班求解完成!")
            self._status = ScheduleSolver.AUX_OPTIMAL
            return self._status

    def calc_nextday_result(self, var_matrix):
        for i in range(self.staff_cnt):
            for k in range(self.aux_worktime_cnt):
                staff = self._staff_list[i]
                work_date = self._start_date

                if var_matrix[i][k].solution_value() == 1:
                    work_time = self._aux_shift.worktime_list[k]
                    schedule_task = ScheduleTask(
                        work_date=work_date,
                        staff=staff,
                        work_time=work_time,
                        shift=self._main_shift,
                        is_overwork=True,
                    )
                    staff.add_schedule(schedule_task)
                    break
        self._status = ScheduleSolver.AUX_OPTIMAL

    def enable_output(self) -> None:
        self._elaborate = True

    def print_schedule_info(self):
        print("==========")
        print("员工排班数据：")
        for staff in self._staff_list:
            print("==")
            print(f"员工 Id: {staff.id}")
            print(staff.schedule_summary["summary_str"])
            print(staff.schedule_summary["worklist"])

        print()
        print()
        print("==========")
        print("每日排班数据：")
        for (date, info) in sorted(list(self.days_summary.items())):
            print(f"日期: {date}")
            summary_str = ""
            for (key, value) in sorted(list(info.items())):
                summary_str += f"{key}: {value}人， "
            print(summary_str)

    def calc_return_data(self):
        if (self._aux_shift and self._status == ScheduleSolver.AUX_OPTIMAL) or \
                ((not self._aux_shift) and self._status == ScheduleSolver.MAIN_OPTIMAL):
            # 数据格式转换，按照接口协议修改
            result = {}
            result["appId"] = 1
            result["arrangeCount"] = 0
            result["arrangeDto"] = []
            result["eqIndex"] = 1
            result["messageId"] = None
            result["overTimeDto"] = []

            id = 1
            for staff in self._staff_list:
                for schedule_task in staff.work_schedule:
                    if schedule_task.work_time.id != RelaxId:
                        result["arrangeCount"] += 1

                        detail = {
                            "areaId": -1,
                            "departmentId": self._department.id,
                            "groupId": -1,
                            "id": id,
                            "isLeader": 0,
                            "staffId": staff.id,
                            "workDate": str(schedule_task.work_date),
                            "workDetailDto": [
                                {"startTime": str(datetime.combine(
                                    schedule_task.work_date, time_section.start_time)), 
                                "endTime": str(datetime.combine(
                                    schedule_task.work_date + timedelta(days=int(time_section.is_cross_day)), time_section.end_time))} 
                                for time_section in schedule_task.work_time.time_section_list
                            ],
                            "workTimeId": schedule_task.work_time.id,
                            "isMain": not schedule_task.is_overwork
                        }
                        result["arrangeDto"].append(detail)

                        id += 1
            return result

        else:
            raise Exception("Solver Error.")
            

        
